/*
 * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include <stdint.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/soc_caps.h"
#include "driver/i2s_pdm.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "sdkconfig.h"
#include "i2s_pdm_example.h"
#include "i2s_example_pins.h"
#include "esp_log.h"

#define EXAMPLE_PDM_RX_CLK_IO           EXAMPLE_I2S_BCLK_IO1      // I2S PDM RX clock io number
#define EXAMPLE_PDM_RX_DIN_IO           EXAMPLE_I2S_DIN_IO1      // I2S PDM RX data in io number
#if SOC_I2S_PDM_MAX_RX_LINES == 4
#define EXAMPLE_PDM_RX_DIN1_IO          EXAMPLE_I2S_DIN1_IO1      // I2S PDM RX data line1 in io number
#define EXAMPLE_PDM_RX_DIN2_IO          EXAMPLE_I2S_DIN2_IO1      // I2S PDM RX data line2 in io number
#define EXAMPLE_PDM_RX_DIN3_IO          EXAMPLE_I2S_DIN3_IO1      // I2S PDM RX data line3 in io number
#endif

#if SOC_I2S_SUPPORTS_PDM2PCM
#define EXAMPLE_PDM_RX_FREQ_HZ          16000           // I2S PDM RX frequency in PCM format
#else
#define EXAMPLE_PDM_RX_FREQ_HZ          2048000         // I2S PDM RX over sample frequency in raw PDM format
#endif

static const char *TAG = "i2s_pdm_rx";

static i2s_chan_handle_t i2s_example_init_pdm_rx(void)
{
#if SOC_I2S_SUPPORTS_PDM2PCM
    ESP_LOGI(TAG, "I2S PDM RX example (receiving data in PCM format)");
#else
    ESP_LOGI(TAG, "I2S PDM RX example (receiving data in raw PDM format)");
#endif  // SOC_I2S_SUPPORTS_PDM2PCM
    i2s_chan_handle_t rx_chan;        // I2S rx channel handler
    /* Setp 1: Determine the I2S channel configuration and allocate RX channel only
     * The default configuration can be generated by the helper macro,
     * but note that PDM channel can only be registered on I2S_NUM_0 */
    i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));

    /* Step 2: Setting the configurations of PDM RX mode and initialize the RX channel
     * The slot configuration and clock configuration can be generated by the macros
     * These two helper macros is defined in 'i2s_pdm.h' which can only be used in PDM RX mode.
     * They can help to specify the slot and clock configurations for initialization or re-configuring */
    i2s_pdm_rx_config_t pdm_rx_cfg = {
        .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(EXAMPLE_PDM_RX_FREQ_HZ),
        /* The data bit-width of PDM mode is fixed to 16 */
#if SOC_I2S_SUPPORTS_PDM2PCM
        .slot_cfg = I2S_PDM_RX_SLOT_PCM_FMT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
#else
        // For the target that not support PDM-to-PCM format, we can only receive RAW PDM data format
        .slot_cfg = I2S_PDM_RX_SLOT_RAW_FMT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
#endif  // SOC_I2S_SUPPORTS_PDM2PCM
        .gpio_cfg = {
            .clk = EXAMPLE_PDM_RX_CLK_IO,
#if SOC_I2S_PDM_MAX_RX_LINES == 4
            .dins = {
                EXAMPLE_PDM_RX_DIN_IO,
                EXAMPLE_PDM_RX_DIN1_IO,
                EXAMPLE_PDM_RX_DIN2_IO,
                EXAMPLE_PDM_RX_DIN3_IO,
            },
#else
            .din = EXAMPLE_PDM_RX_DIN_IO,
#endif
            .invert_flags = {
                .clk_inv = false,
            },
        },
    };
#if CONFIG_IDF_TARGET_ESP32S3
    // Enable all slots for example
    pdm_rx_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO;
    pdm_rx_cfg.slot_cfg.slot_mask = I2S_PDM_LINE_SLOT_ALL;
#endif
    ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg));

    /* Step 3: Enable the rx channels before reading data */
    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
    return rx_chan;
}

void i2s_example_pdm_rx_task(void *args)
{
    int16_t *r_buf = (int16_t *)calloc(1, EXAMPLE_BUFF_SIZE);
    assert(r_buf);
    i2s_chan_handle_t rx_chan = i2s_example_init_pdm_rx();

    size_t r_bytes = 0;
    /* ATTENTION: The print and delay in the read task only for monitoring the data by human,
     * Normally there shouldn't be any delays to ensure a short polling time,
     * Otherwise the dma buffer will overflow and lead to the data lost */
    while (1) {
        /* Read i2s data */
        if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
            printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
            printf("[0] %d [1] %d [2] %d [3] %d\n[4] %d [5] %d [6] %d [7] %d\n\n",
                   r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
        } else {
            printf("Read Task: i2s read failed\n");
        }
        vTaskDelay(pdMS_TO_TICKS(200));
    }
    free(r_buf);
    vTaskDelete(NULL);
}
